%{

#include "declaration.h"

  %}

%union{char *id; struct str{char code[10000];int type; char reg[10000];} st;}
%token<id> CONSTANTI
%token<id> CONSTANTF
%token<id> INC_OP DEC_OP LE_OP GE_OP EQ_OP NE_OP
%token<id> IF ELSE WHILE RETURN FOR

%token<id> IDENTIFIER INT FLOAT VOID TYPE_NAME
%token<id> SUB_ASSIGN MUL_ASSIGN ADD_ASSIGN

%type<st> type_name declarator function_definition compound_statement declaration_list statement_list declarator_list argument_expression_list
%type<st> statement expression_statement expression unary_expression assignment_operator comparison_expression
%type<st> postfix_expression primary_expression multiplicative_expression additive_expression parameter_list unary_operator
%type<st> declaration selection_statement iteration_statement jump_statement program external_declaration parameter_declaration

%start program
%%

primary_expression
: IDENTIFIER           {
  sprintf($$.reg, "%%r%d", new_reg());
  sprintf(tmpC, "%%%s", $1);
  $$.type = tabHash[hash(tmpC)];
  sprintf($$.code, "%s = load %s* %s\n", $$.reg, translate($$.type), tmpC);
  if(affect == 0)
    strcpy(affecType, tmpC);
 } 

| CONSTANTI            {strcpy($$.code, $1); $$.type = ID_INT; strcpy($$.reg, "");}
| CONSTANTF            {sprintf($$.code, "%.*e", OP_DBL_Digs - 1, strtof($1,NULL)); $$.type = ID_FLOAT; strcpy($$.reg, "");}
| '(' expression ')'   {strcpy($$.code, $2.code); strcpy($$.reg, "");}

| IDENTIFIER '(' ')'                          {
  sprintf($$.reg, "%%r%d", new_reg()); 
  sprintf($$.code, "%s = call %s @%s()\n", $$.reg, translate(glob_type), $1);
  sprintf(tabFonction[glob_fonction], "declare %s @%s()", translate(glob_type), $1);
  sprintf(tabFonctionNom[glob_fonction], "%s", $1);
  glob_fonction++;
  $$.type = glob_type;}

| IDENTIFIER '(' argument_expression_list ')' {
  sprintf($$.reg, "%%r%d", new_reg()); 
  sprintf($$.code, "%s\n %s = call %s @%s(%s)\n", postCodeReg($3.reg, $3.code), $$.reg, translate(glob_type), $1, codeReg($3.reg, $3.code));
  parse_arg_list(codeReg($3.reg, $3.code));
  sprintf(tabFonction[glob_fonction], "declare %s @%s(%s)", translate(glob_type), $1, tmpC);
  sprintf(tabFonctionNom[glob_fonction], "%s", $1);
  glob_fonction++;

  $$.type = glob_type;}

| IDENTIFIER INC_OP {
  sprintf($$.reg, "%%r%d", new_reg());
  sprintf(tmpC, "%%%s", $1);
  int i = new_reg();
  $$.type = tabHash[hash(tmpC)];

  if ($$.type == ID_INT)
    sprintf($$.code, "%%r%d = load i32* %s \n %s = add i32 %%r%d, 1\n store i32 %s, i32* %s\n", i, tmpC, $$.reg, i, $$.reg, tmpC);
  else if ($$.type == ID_FLOAT)
    sprintf($$.code, "%%r%d = load float* %s \n %s = fadd float %%r%d, 1.000000e+00 \n store float %s, float* %s\n", i, tmpC, $$.reg, i, $$.reg, tmpC);
  else{
    yyerror(NULL);
    exit(EXIT_FAILURE);}}

| IDENTIFIER DEC_OP {
  sprintf($$.reg, "%%r%d", new_reg());
  sprintf(tmpC, "%%%s", $1);
  int i = new_reg();
  $$.type = tabHash[hash(tmpC)];

  if ($$.type == ID_INT)
    sprintf($$.code, "%%r%d = load i32* %s \n %s = sub i32 %%r%d, 1\n store i32 %s, i32* %s\n", i, tmpC, $$.reg, i, $$.reg, tmpC);
  else if ($$.type == ID_FLOAT)
    sprintf($$.code, "%%r%d = load float* %s \n %s = fsub float %%r%d, 1.000000e+00 \n store float %s, float* %s\n", i, tmpC, $$.reg, i, $$.reg, tmpC);
  else{
    yyerror(NULL);
    exit(EXIT_FAILURE);}}
;

postfix_expression
: primary_expression                    {
  strcpy($$.code, $1.code); 
  strcpy($$.reg, $1.reg);
  $$.type = $1.type;
  glob_type = $1.type;}

| postfix_expression '[' expression ']' {
  int i = new_reg();
  sprintf($$.reg, "%%r%d", new_reg());
  $$.type = $1.type;
  sprintf($$.code, "%%r%d = getelementptr %s* %s, %s %s \n %s = load %s* %%r%d \n", i, translate($1.type), $1.reg, translate($3.type), codeReg($3.reg, $3.code), $$.reg, translate($1.type), i);
  }
;

argument_expression_list
: primary_expression                                {sprintf($$.code, "%s", postCodeReg($1.reg, $1.code)); sprintf($$.reg, "%s %s", translate($1.type), codeReg($1.reg, $1.code)); $$.type = $1.type;}
| argument_expression_list ',' primary_expression   {sprintf($$.code, "%s\n %s", $1.code, postCodeReg($3.reg, $3.code)); sprintf($$.reg, "%s,%s %s", $1.reg, translate($3.type), codeReg($3.reg, $3.code)); $$.type = $1.type;}
;

unary_expression
: postfix_expression                {strcpy($$.code, $1.code); strcpy($$.reg, $1.reg); $$.type = $1.type;}

| INC_OP unary_expression { 
  sprintf($$.reg, "%%%d", new_reg()); 
  $$.type = $2.type;
  if ($2.type == 0)
    sprintf($$.code, "%s \n %s = add %s, 1\n", $2.code, $$.reg, $2.reg);
  else if ($2.type == 1)
    sprintf($$.code, "%s \n %s = fadd %s, 1.000000e+00\n", $2.code, $$.reg, $2.reg);
  else{
     yyerror(NULL);
     exit(EXIT_FAILURE);}}
 
| DEC_OP unary_expression { 
  sprintf($$.reg, "%%%d", new_reg()); 
  $$.type = $2.type;
  if ($2.type == 0)
    sprintf($$.code, "%s \n %s = sub %s, 1\n", $2.code, $$.reg, $2.reg);
  else if ($2.type == 1)
    sprintf($$.code, "%s \n %s = fsub %s, 1.000000e+00\n", $2.code, $$.reg, $2.reg);
  else{
    yyerror(NULL);
    exit(EXIT_FAILURE);}}

| unary_operator unary_expression {
  sprintf(tmpC, "%%%d", new_reg()); strcpy($$.reg,tmpC);
  $$.type = $2.type;
  if ($2.type == 0)
    sprintf($$.code, "%s \n %s = sub 0, %s\n", $1.code, $$.reg, $2.reg);
  else if ($2.type == 1)
    sprintf($$.code, "%s \n %s = fsub 0.000000e+00, %s\n", $1.code, $$.reg, $2.reg);
  else{
    yyerror(NULL);
    exit(EXIT_FAILURE);}}
;

unary_operator
: '-' {strcpy($$.code, "-");}
;


multiplicative_expression
: unary_expression                                {strcpy($$.reg, $1.reg); strcpy($$.code, $1.code); $$.type = $1.type;}

| multiplicative_expression '*' unary_expression  {
  sprintf($$.reg, "%%r%d", new_reg()); 
  $$.type = $1.type;

  if(($1.type == ID_INT)&&($3.type == ID_INT)){
    sprintf($$.code, "%s \n %s \n %s = mul i32 %s, %s\n", postCodeReg($1.reg, $1.code), postCodeReg($3.reg, $3.code), $$.reg, codeReg($1.reg, $1.code), codeReg($3.reg, $3.code));

  }else if(($1.type == ID_FLOAT)&&($3.type == ID_FLOAT)){
    sprintf($$.code, "%s \n %s \n %s = fmul float %s, %s\n", postCodeReg($1.reg, $1.code), postCodeReg($3.reg, $3.code), $$.reg, codeReg($1.reg, $1.code), codeReg($3.reg, $3.code));

  }else{
    printf("Erreur de compatibilité *\n");
    yyerror(NULL);
    exit(EXIT_FAILURE);
  }}

| multiplicative_expression '/' unary_expression  {
  sprintf($$.reg, "%%r%d", new_reg()); 
  $$.type = $1.type;

  if(($1.type == ID_INT)&&($3.type == ID_INT)){
    sprintf($$.code, "%s \n %s \n %s = div i32 %s, %s\n", postCodeReg($1.reg, $1.code), postCodeReg($3.reg, $3.code), $$.reg, codeReg($1.reg, $1.code), codeReg($3.reg, $3.code));

  }else if(($1.type == ID_FLOAT)&&($3.type == ID_FLOAT)){
    sprintf($$.code, "%s \n %s \n %s = fdiv float %s, %s\n", postCodeReg($1.reg, $1.code), postCodeReg($3.reg, $3.code), $$.reg, codeReg($1.reg, $1.code), codeReg($3.reg, $3.code));

  }else{
    printf("Erreur de compatibilité /\n");
    yyerror(NULL);
    exit(EXIT_FAILURE);
  }}
;

additive_expression
: multiplicative_expression {
  strcpy($$.reg, $1.reg); 
  strcpy($$.code, $1.code); 
  $$.type = $1.type;}

| additive_expression '+' multiplicative_expression   {
  sprintf($$.reg, "%%r%d", new_reg()); 
  $$.type = $1.type;

  if(($1.type == ID_INT)&&($3.type == ID_INT)){
    sprintf($$.code, "%s \n %s \n %s = add i32 %s, %s\n", postCodeReg($1.reg, $1.code), postCodeReg($3.reg, $3.code), $$.reg, codeReg($1.reg, $1.code), codeReg($3.reg, $3.code));

  }else if(($1.type == ID_FLOAT)&&($3.type == ID_FLOAT)){
    sprintf($$.code, "%s \n %s \n %s = fadd float %s, %s\n", postCodeReg($1.reg, $1.code), postCodeReg($3.reg, $3.code), $$.reg, codeReg($1.reg, $1.code), codeReg($3.reg, $3.code));

  }else{
    printf("Erreur de compatibilité +\n");
    yyerror(NULL);
    exit(EXIT_FAILURE);
  }}

| additive_expression '-' multiplicative_expression   {
  sprintf($$.reg, "%%r%d", new_reg()); 
  $$.type = $1.type;

  if(($1.type == ID_INT)&&($3.type == ID_INT)){
    sprintf($$.code, "%s \n %s \n %s = sub i32 %s, %s\n", postCodeReg($1.reg, $1.code), postCodeReg($3.reg, $3.code), $$.reg, codeReg($1.reg, $1.code), codeReg($3.reg, $3.code));

  }else if(($1.type == ID_FLOAT)&&($3.type == ID_FLOAT)){
    sprintf($$.code, "%s \n %s \n %s = fsub float %s, %s\n", postCodeReg($1.reg, $1.code), postCodeReg($3.reg, $3.code), $$.reg, codeReg($1.reg, $1.code), codeReg($3.reg, $3.code));

  }else{
    printf("Erreur de compatibilité -\n");
    yyerror(NULL);
    exit(EXIT_FAILURE);
  }}
;

comparison_expression
: additive_expression                           {strcpy($$.reg, $1.reg); strcpy($$.code, $1.code); $$.type = $1.type;}

| additive_expression '<' additive_expression   {
  sprintf($$.reg, "%%r%d", new_reg()); 
  $$.type = $1.type;

  if(($1.type == ID_INT)&&($3.type == ID_INT)){
    sprintf($$.code, "%s \n %s \n %s = icmp slt i32 %s, %s\n", postCodeReg($1.reg, $1.code), postCodeReg($3.reg, $3.code), $$.reg, codeReg($1.reg, $1.code), codeReg($3.reg, $3.code));

  }else if(($1.type == ID_FLOAT)&&($3.type == ID_FLOAT)){
    sprintf($$.code, "%s \n %s \n %s = fcmp olt float %s, %s\n", postCodeReg($1.reg, $1.code), postCodeReg($3.reg, $3.code), $$.reg, codeReg($1.reg, $1.code), codeReg($3.reg, $3.code));

  }else{
    printf("Les comparaisons doivent se faire entre deux types identiques (6)\n");
    yyerror(NULL);
    exit(EXIT_FAILURE);
  }}

| additive_expression '>' additive_expression   {
  sprintf($$.reg, "%%r%d", new_reg()); 
  $$.type = $1.type;

  if(($1.type == ID_INT)&&($3.type == ID_INT)){
    sprintf($$.code, "%s \n %s \n %s = icmp sgt i32 %s, %s\n", postCodeReg($1.reg, $1.code), postCodeReg($3.reg, $3.code), $$.reg, codeReg($1.reg, $1.code), codeReg($3.reg, $3.code));

  }else if(($1.type == ID_FLOAT)&&($3.type == ID_FLOAT)){
    sprintf($$.code, "%s \n %s \n %s = fcmp ogt float %s, %s\n", postCodeReg($1.reg, $1.code), postCodeReg($3.reg, $3.code), $$.reg, codeReg($1.reg, $1.code), codeReg($3.reg, $3.code));

  }else{
    printf("Les comparaisons doivent se faire entre deux types identiques (5)\n");
    yyerror(NULL);
    exit(EXIT_FAILURE);
  }}

| additive_expression LE_OP additive_expression {
  sprintf($$.reg, "%%r%d", new_reg()); 
  $$.type = $1.type;

  if(($1.type == ID_INT)&&($3.type == ID_INT)){
    sprintf($$.code, "%s \n %s \n %s = icmp sle i32 %s, %s\n", postCodeReg($1.reg, $1.code), postCodeReg($3.reg, $3.code), $$.reg, codeReg($1.reg, $1.code), codeReg($3.reg, $3.code));

  }else if(($1.type == ID_FLOAT)&&($3.type == ID_FLOAT)){
    sprintf($$.code, "%s \n %s \n %s = fcmp ole float %s, %s\n", postCodeReg($1.reg, $1.code), postCodeReg($3.reg, $3.code), $$.reg, codeReg($1.reg, $1.code), codeReg($3.reg, $3.code));

  }else{
    printf("Les comparaisons doivent se faire entre deux types identiques (4)\n");
    yyerror(NULL);
    exit(EXIT_FAILURE);
  }}

| additive_expression GE_OP additive_expression {
  sprintf($$.reg, "%%r%d", new_reg()); 
  $$.type = $1.type;

  if(($1.type == ID_INT)&&($3.type == ID_INT)){
    sprintf($$.code, "%s \n %s \n %s = icmp sge i32 %s, %s\n", postCodeReg($1.reg, $1.code), postCodeReg($3.reg, $3.code), $$.reg, codeReg($1.reg, $1.code), codeReg($3.reg, $3.code));

  }else if(($1.type == ID_FLOAT)&&($3.type == ID_FLOAT)){
    sprintf($$.code, "%s \n %s \n %s = fcmp oge float %s, %s\n", postCodeReg($1.reg, $1.code), postCodeReg($3.reg, $3.code), $$.reg, codeReg($1.reg, $1.code), codeReg($3.reg, $3.code));

  }else{
    printf("Les comparaisons doivent se faire entre deux types identiques (3)\n");
    yyerror(NULL);
    exit(EXIT_FAILURE);
  }}

| additive_expression EQ_OP additive_expression {
  sprintf($$.reg, "%%r%d", new_reg()); 
  $$.type = $1.type;

  if(($1.type == ID_INT)&&($3.type == ID_INT)){
    sprintf($$.code, "%s \n %s \n %s = icmp eq i32 %s, %s\n", postCodeReg($1.reg, $1.code), postCodeReg($3.reg, $3.code), $$.reg, codeReg($1.reg, $1.code), codeReg($3.reg, $3.code));

  }else if(($1.type == ID_FLOAT)&&($3.type == ID_FLOAT)){
    sprintf($$.code, "%s \n %s \n %s = fcmp oeq float %s, %s\n", postCodeReg($1.reg, $1.code), postCodeReg($3.reg, $3.code), $$.reg, codeReg($1.reg, $1.code), codeReg($3.reg, $3.code));

  }else{
    printf("Les comparaisons doivent se faire entre deux types identiques (2)\n");
    yyerror(NULL);
    exit(EXIT_FAILURE);
  }}

| additive_expression NE_OP additive_expression {
  sprintf($$.reg, "%%r%d", new_reg()); 
  $$.type = $1.type;

  if(($1.type == ID_INT)&&($3.type == ID_INT)){
    sprintf($$.code, "%s \n %s \n %s = icmp ne i32 %s, %s\n", postCodeReg($1.reg, $1.code), postCodeReg($3.reg, $3.code), $$.reg, codeReg($1.reg, $1.code), codeReg($3.reg, $3.code));

  }else if(($1.type == ID_FLOAT)&&($3.type == ID_FLOAT)){
    sprintf($$.code, "%s \n %s \n %s = fcmp one float %s, %s\n", postCodeReg($1.reg, $1.code), postCodeReg($3.reg, $3.code), $$.reg, codeReg($1.reg, $1.code), codeReg($3.reg, $3.code));

  }else{
    printf("Les comparaisons doivent se faire entre deux types identiques (1)\n");
    yyerror(NULL);
    exit(EXIT_FAILURE);
  }}
;

expression
: unary_expression assignment_operator comparison_expression  {
  int j;
  if(($1.type == RO_INT)||($1.type == RO_FLOAT)){
    printf("Ecriture dans variable en lecture seule impossible\n");
    yyerror(NULL);
    exit(EXIT_FAILURE);
  }
  if(strcmp($3.reg,"")==0)
    strcpy($$.code, "");
  else
    strcpy($$.code, $3.code);
  
  if(strcmp($2.code,"+=")==0){
    int i = new_reg();
    sprintf($$.reg, "%%r%d", new_reg());
    if ($$.type == ID_INT)
      sprintf(tmpC, "%%r%d = load i32* %s \n %s = add i32 %%r%d, %s\n store i32 %s, i32* %s\n", i, affecType, $$.reg, i, codeReg($3.reg, $3.code), $$.reg, affecType);
    else if ($$.type == ID_FLOAT)
      sprintf(tmpC, "%%r%d = load float* %s \n %s = fadd float %%r%d, %s \n store float %s, float* %s\n", i, affecType, $$.reg, i, codeReg($3.reg, $3.code), $$.reg, affecType);
    else{
      yyerror(NULL);
      exit(EXIT_FAILURE);}
    strcat($$.code, tmpC);
  }

  else if(strcmp($2.code,"-=")==0){
    int i = new_reg();
    sprintf($$.reg, "%%r%d", new_reg());
    if ($$.type == ID_INT)
      sprintf(tmpC, "%%r%d = load i32* %s \n %s = sub i32 %%r%d, %s\n store i32 %s, i32* %s\n", i, affecType, $$.reg, i, codeReg($3.reg, $3.code), $$.reg, affecType);
    else if ($$.type == ID_FLOAT)
      sprintf(tmpC, "%%r%d = load float* %s \n %s = fsub float %%r%d, %s \n store float %s, float* %s\n", i, affecType, $$.reg, i, codeReg($3.reg, $3.code), $$.reg, affecType);
    else{
      yyerror(NULL);
      exit(EXIT_FAILURE);}
    strcat($$.code, tmpC);
  }

  else if(strcmp($2.code,"*=")==0){
    int i = new_reg();
    sprintf($$.reg, "%%r%d", new_reg());
    sprintf(tmpC, "%%r%d = load float* %s \n %s = fmul float %%r%d, %s \n store float %s, float* %s\n", i, affecType, $$.reg, i, codeReg($3.reg, $3.code), $$.reg, affecType);
    strcat($$.code, tmpC);
  }
  else {
    if($1.type == $3.type){
      sprintf(tmpC, "store %s %s, %s* %s\n", translate($1.type), codeReg($3.reg, $3.code), translate($1.type), affecType);
      strcat($$.code, tmpC);
      if(((j=in_static_array(affecType))!= -1)&&(!static_var[j])){
  static_var[j]=1;
      }
    }
    else {
      printf("L'affectation doit se faire entre deux types identiques\n ");
      yyerror(NULL);
      exit(EXIT_FAILURE);
    }
  }
  affect = 0;
 }


| comparison_expression  {strcpy($$.code, $1.code); strcpy($$.reg, $1.reg);}
;

assignment_operator
: '='           {strcpy($$.code, "="); affect = 1;}
| MUL_ASSIGN    {strcpy($$.code, $1); affect = 1;}
| ADD_ASSIGN    {strcpy($$.code, $1); affect = 1;}
| SUB_ASSIGN    {strcpy($$.code, $1); affect = 1;}
;

declaration
: type_name declarator_list ';'  { 
  strcpy($$.code, $2.code);
 }
;

declarator_list
: declarator                        {
  if($$.type == ID_TAB) 
    strcpy($$.code, $1.code);
  else if ($$.type == NOINIT)
    strcpy($$.code, "");
  else
    sprintf($$.code, "%s = alloca %s \n", $1.code, translate(tabHash[hash($1.code)]));
 }

| declarator_list ',' declarator    {
   if($$.type == ID_TAB)
    sprintf($$.code, "%s %s\n", $1.code, $3.code);
  else
    sprintf($$.code, "%s %s = alloca %s \n", $1.code, $3.code, translate(tabHash[hash($3.code)]));
}
;

type_name
: VOID {
  glob_type = ID_VOID;
  strcpy($$.code, "void");
 }
| INT {
  glob_type = ID_INT;
  strcpy($$.code, "i32");
  }
| FLOAT {
  glob_type = ID_FLOAT;
  strcpy($$.code, "float");
  }
;

declarator
: IDENTIFIER                          {
  sprintf(tmpC, "%%%s", $1);
  if(tabHash[hash(tmpC)] == NOINIT){ 
    tabHash[hash(tmpC)] = glob_type; 
    strcpy($$.code, tmpC);
    strcpy($$.reg, tmpC);
    $$.type = glob_type;
  } 
  else {
    printf("Erreur : variable %s deja declaree (ou probleme de table de hashage)\n", tmpC); 
    yyerror(NULL); 
    exit(EXIT_FAILURE);}}

| '(' declarator ')'            {
  if(strcmp($2.code, "drive")==0) 
    strcpy($$.code, $2.code); 
  else
    sprintf($$.code, "(%s)", $2.code);
  $$.type = $2.type;}

| declarator '[' CONSTANTI ']'  {
  if(strcmp($1.code, "drive")==0){ 
    strcpy($$.code, $1.code);
    $$.type = $1.type;
  }
  else{
    sprintf($$.code, "%s = alloca [%s x %s]\n", $1.reg, $3, translate(tabHash[hash($1.reg)])); 
    strcpy($$.reg, $1.reg); 
    $$.type = ID_TAB;
  }}

| declarator '[' ']'  {
  if(strcmp($1.code, "drive")==0)
    strcpy($$.code, $1.code+1); 
  else{
    sprintf(tmpC, "%s [ ]", $1.code); 
    strcpy($$.code, tmpC);
  }
  $$.type = $1.type;}

| declarator '(' parameter_list ')'   {
  if(strcmp($1.code, "drive")==0)
    strcpy($$.code, $1.code+1); 
  else{
    sprintf(tmpC, "%s ( %s )", $1.code+1, $3.code); 
    strcpy($$.code, tmpC);
  }
  $$.type = NOINIT;}

| declarator '(' ')'                  {if(strcmp($1.code, "%drive")==0) strcpy($$.code, $1.code+1); else{sprintf(tmpC, "%s ( )", $1.code+1); strcpy($$.code, tmpC);}
   $$.type = NOINIT;}
;

parameter_list
: parameter_declaration {
  strcpy($$.code, $1.code); 
  $$.type = $1.type;
 }

| parameter_list ',' parameter_declaration  {
  sprintf(tmpC, "%s , %s", $1.code, $3.code); 
  strcpy($$.code, tmpC); 
  $$.type = $1.type;
  }
;

parameter_declaration
: type_name declarator    {
  $$.type = glob_type;
  if(glob_type == ID_FLOAT){
    sprintf(tmpC, "float %s",$2.code); 
    strcpy($$.code, tmpC);
  }
  else if(glob_type == ID_VOID){
    sprintf(tmpC, "void %s",$2.code); 
    strcpy($$.code, tmpC);
  }
  else if(glob_type == ID_INT){
    sprintf(tmpC, "i32 %s",$2.code); 
    strcpy($$.code, tmpC);
  }
  else exit(EXIT_FAILURE);
 }
;

statement
: compound_statement   {strcpy($$.code, $1.code); $$.type = $1.type;}
| expression_statement {strcpy($$.code, $1.code); $$.type = $1.type;}
| selection_statement  {strcpy($$.code, $1.code); $$.type = $1.type;}
| iteration_statement  {strcpy($$.code, $1.code); $$.type = $1.type;}
| jump_statement       {strcpy($$.code, $1.code); $$.type = $1.type;}
;

compound_statement
: '{' '}'               {strcpy($$.code, "");}
| '{' statement_list '}' {strcpy($$.code, $2.code); $$.type = $2.type;}
| '{' declaration_list statement_list '}' {
  strcpy($$.code, $2.code); strcat($$.code, $3.code); $$.type = $2.type;
  }
;

declaration_list
: declaration                        {strcpy($$.code, $1.code); $$.type = $1.type;}
| declaration_list declaration       {sprintf($$.code, "%s \n%s", $1.code, $2.code);}
;

statement_list
: statement                          {strcpy($$.code, $1.code);}
| statement_list statement           {tmp = new_reg(); sprintf($$.code, "%s \n%s", $1.code, $2.code);}
;

expression_statement
: ';'                {strcpy($$.code, "");}
| expression ';'     {strcpy($$.code, $1.code);}
;

selection_statement
: IF '(' expression ')' statement {
  tmp = new_reg(); 
  sprintf($$.code, "%s \n br i1 %s, label %%ifvrai%d, label %%endif%d \n\n ifvrai%d:%s \n br label %%endif%d \n  endif%d: \n\n", $3.code, $3.reg, tmp, tmp, tmp, $5.code, tmp, tmp); 
  $$.type = $3.type;} 

| IF '(' expression ')' statement ELSE statement {
  tmp = new_reg(); 
  sprintf($$.code, "%s \n br i1 %s, label %%ifvrai%d, label %%iffaux%d \n\n ifvrai%d:%s \n br label %%endif%d \n iffaux%d:%s \n br label %%endif%d \n endif%d: \n", $3.code, $3.reg, tmp, tmp, tmp, $5.code, tmp, tmp, $7.code, tmp, tmp);
  $$.type = $3.type;}

| FOR '(' expression_statement expression_statement expression ')' statement  {
  tmp = new_reg(); 
  sprintf($$.code, "%s \n br label %%ifvrai%d \n ifvrai%d: \n %s \n %s \n %s \n br i1 %s, label %%ifvrai%d, label %%iffaux%d  \n iffaux%d: \n br label %%endfor%d \n endfor%d: \n",$3.code, tmp, tmp, $7.code, $5.code, $4.code, $4.reg, tmp, tmp, tmp, tmp, tmp); 
  $$.type = $3.type;} 
;

iteration_statement
: WHILE '(' expression ')' statement  {tmp = new_reg(); sprintf($$.code, "br label %%while%d \n while%d: \n %s \n %s \n br i1 %s, label %%while%d, label %%wend%d \n wend%d:\n\n", tmp, tmp, $5.code, $3.code, $3.reg, tmp, tmp, tmp);}
;

jump_statement
: RETURN ';'            {strcpy($$.code, "ret void");}
| RETURN expression ';' {
  strcpy($$.code, $2.code);
  strcat($$.code, "\n");
  strcat($$.code, "ret "); if($2.type==4) strcat($$.code, "i32 ");
   else if($2.type==1) strcat($$.code, "float ");
   else{
     yyerror(NULL);
     exit(EXIT_FAILURE);
   }
   if(strcmp($2.reg,"")==0)
     strcat($$.code, $2.code);
   else
     strcat($$.code, $2.reg);

 }
;

program
: external_declaration         {strcpy($$.code, $1.code);}
| program external_declaration {tmp = new_reg(); sprintf(tmpC, "%s \n%s", $1.code, $2.code); strcpy($$.code, tmpC);}
;

external_declaration
: function_definition          {strcpy($$.code, $1.code);}
| declaration                  {strcpy($$.code, $1.code);}
;

function_definition
: type_name declarator compound_statement                        
{   tmp = new_reg(); 
  if(strcmp($2.code, "drive")==0){
    strcpy($2.code, "drive(i32 %index, %struct.CarElt* %car, %struct.Situation* %s)");
    strcat(elseDrive, "{\n");
    strcat(elseDrive, "\n");
    strcat(elseDrive, "    %ctrl   = getelementptr %struct.CarElt* %car, i32 0, i32 5 \n");
    strcat(elseDrive, "    %public_car = getelementptr %struct.CarElt* %car, i32 0, i32 2 \n");
    strcat(elseDrive, "    %pos    = getelementptr %struct.tPublicCar* %public_car, i32 0, i32 3 \n");
    strcat(elseDrive, "    %seg.addr = getelementptr %struct.tTrkLocPos* %pos, i32 0, i32 0 \n");
    strcat(elseDrive, "    %seg    = load %struct.trackSeg** %seg.addr \n");
    strcat(elseDrive, "\n");
    strcat(elseDrive, "    %steer    = getelementptr %struct.tCarCtrl* %ctrl, i32 0, i32 0 \n");
    strcat(elseDrive, "    %accelCmd = getelementptr %struct.tCarCtrl* %ctrl, i32 0, i32 1 \n");
    strcat(elseDrive, "    %brakeCmd = getelementptr %struct.tCarCtrl* %ctrl, i32 0, i32 2 \n");
    strcat(elseDrive, "    %clutchCmd  = getelementptr %struct.tCarCtrl* %ctrl, i32 0, i32 3 \n");
    strcat(elseDrive, "    %gear   = getelementptr %struct.tCarCtrl* %ctrl, i32 0, i32 4 \n");
    strcat(elseDrive, "\n");
    // strcat(elseDrive, "    %road_angle = call float @get_track_angle(%struct.tTrkLocPos* %pos) \n");
    //strcat(elseDrive, "    %car_angle  = call float @get_car_yaw(%struct.CarElt* %car) \n");
    //strcat(elseDrive, "    %angle    = fsub float %road_angle, %car_angle \n");
    //strcat(elseDrive, "    %nangle   = call float @norm_pi_pi(float %angle) \n");
    //strcat(elseDrive, "\n");
    //strcat(elseDrive, "    %posmid   = call float @get_pos_to_middle(%struct.tTrkLocPos* %pos) \n");
    //strcat(elseDrive, "    %width    = call float @get_track_seg_width(%struct.trackSeg* %seg) \n");
    //strcat(elseDrive, "    %corr   = fdiv float %posmid, %width \n");
    //strcat(elseDrive, "    %cangle   = fsub float %nangle, %corr \n");
    //strcat(elseDrive, "\n");
    //strcat(elseDrive, "    store float %cangle, float* %steer \n    ");
    strcat(elseDrive, $3.code);
    strcat(elseDrive, "\n");
       ajout_store_manquant();
    strcat(elseDrive, tmpC);
    strcat(elseDrive, "\n \n    ret void \n");
    strcat(elseDrive, "}\n");
  } else {
    strcpy($$.code, $2.code);
    strcat(elseDrive, "{\n");
    strcat(elseDrive, "\n");
    strcat(elseDrive, $3.code); 
    strcat(elseDrive, "}\n");
  } 
                          
  fprintf(driver,"define %s @%s %s\n\n", $1.code, $2.code, elseDrive);
  elseDrive[0] = '\0';} // tester declarator !!
;

%%
#include <stdio.h>
#include <string.h>

extern int column;
extern int yylineno;
extern FILE *yyin;

char *file_name = NULL;

int yyerror (char *s) {
  fflush (stdout);
  fprintf (stderr, "%s:%d:%d: %s\n", file_name, yylineno, column, s);
  return 0;
}

int main (int argc, char *argv[]) {
  FILE *input = NULL;
  //FILE *fopen (const char *path, const char *mode);

  if (argc==2) {
    input = fopen (argv[1], "r");
    file_name = strdup (argv[1]);
    if (input) {
      yyin = input;
    }
    else {
      fprintf (stderr, "%s: Could not open %s\n", *argv, argv[1]);
      return 1;
    }
  }
  else {
    fprintf (stderr, "%s: error: no input file\n", *argv);
    return 1;
  }
  driver = fopen("./drive.ll", "w");
  init_hash();
  static_declarations();
  allExceptDrive();
  yyparse ();
  declarations();
  free (file_name);
  fclose(driver);
  return 0;
}
